import Vue from 'vue'
import Router from 'vue-router'
import VueCookies from 'vue-cookies'
import gv from '@/utils/global-var'
import http from '@/utils/request'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'

/**
 * Note: sub-menu only appear when route children.length >= 1
 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 *
 * hidden: true                   if set true, item will not show in the sidebar(default is false)
 * alwaysShow: true               if set true, will always show the root menu
 *                                if not set alwaysShow, when item has more than one children route,
 *                                it will becomes nested mode, otherwise not show the root menu
 * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
    roles: ['admin','editor']    control the page roles (you can set multiple roles)
    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
    icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
  }
 */

/**
 * constantRoutes
 * a base page that does not have permission requirements
 * all roles can be accessed
 */
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  }
  /**
   * 1.对于一级菜单，component是一个固定值：Layout
   * 2.meta下的title是用来控制菜单的名字；
   * 3.children表示，第一个作用表示子菜单；第二个作用就是子路由；
   * 4.redirect: '/sys/user' 表示如果用户访问的路径是 /sys, 就去访问 /sys/user, 就是访问其第一个子菜单；
   */
//    {
//         "path": "/sys",
//         "component": "Layout",
//         "name": "system",
//         "meta": {
//             "title": "系统管理",
//             "icon": "el-icon-setting"
//         },
//         "children": [
//             {
//                 "path": "/sys/user",
//                 "component": "user",  // () => import()
//                 "name": "user",
//                 "meta": {
//                     "title": "用户管理",
//                     "icon": "el-icon-s-custom"
//                 }
//             },
//             {
//                 "path": "/sys/dept",
//                 "component": () => import('@/views/dept/index'),  // () => import()
//                 "name": "user",
//                 "meta": {
//                     "title": "部门管理",
//                     "icon": "el-icon-s-custom"
//                 }
//             }
//         ]
//     },
  // 404 page must be placed at the end !!!=
]

const notFound = { path: '*', redirect: '/404', hidden: true }

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

/**
 * to: 是一个对象，表示要到哪里去，最常用的就是他的 path属性
 * from: 从哪里来，也是一个对象，但是不用；
 * next: 是一个方法，表示要不下往下走
 */
router.beforeEach(async (to, from, next) => {
    console.log(to.path)
    // 如果用户是去登录页面，
    if(to.path === '/login'){  
        next()
    // 已经登录(cookie中有access_token和username, 在登录那里设置的)
    }else if((VueCookies.get('access_token') && VueCookies.get('username'))) {
        /**
         * 因为菜单是动态的，所以要看具体的情况去分析是否从后台去获取菜单：
         *   1.如果是正常切换菜单，没必须获取菜单；
         *   2.刷新页面，因为内存的数都清空，菜单数据也被清空，所以需要重新拿；
         *   3.用户在登录成功后，首次调到首页，拿菜单；
         */
        if(!gv.flag) {
          console.log('动态获取菜单=========')
          /**
          // 获取菜单, 请求头必须要携带 Authorization, 但是这种方式引发一个问题，每个请求都
          // 需要这么配置一下，那么这种做法是不合理的. 那么就在axios全局中每次请求都添加一个 Authorization这样一个
          // 请求头
          const r = await http.get('/sys-menu', {
              headers: {
                  Authorization: VueCookies.get('access_token')
              }
          // })
          */
          // 因为我们通过 axios 全局携带 Authorization, 那么就不需要在每次请求中携带jwt
          const r = await http.get('/sys-menu')
          console.log('获取路由数据: ', r);
          
          // 将flag 值改为true, 用户以后只要不刷新，flag永远为true, 那么这个if就进不来了
          gv.flag = true

          // 将服务端所有的路由信息中组件字符串转换为对应的组件
          convertRts(r)
          /**
           * 如下这种写法是有问题的，r.data是数组，导致结果的 constantRoutes成为了一个二维数组：
           * 例如：const arr = [3, 4]; const arr1 = [56, 78];  arr.push(arr1); arr ==> [3, 4, [56, 78]]
           * constantRoutes.push(r.data)  
           */
          // 只保留前三项(前三项是固定的，后面的菜单是动态)
          constantRoutes.splice(3, constantRoutes.length - 3)

          r.forEach(rt => constantRoutes.push(rt))

          // 将最后的404添加到最后
          constantRoutes.push(notFound)

          // 更新内存中路由表，更新路由表示一个异步更新的过程
          router.addRoutes(r, notFound)

          /**
           * 用户刷新，因为  router.addRoutes() 是异步操作，如果直接 next(), 内存中的路由列表
           * 可能没有更新完毕，例如 /sys/user 这个路径在异步添加的时候还没有添加到路由列表，如果直接next() 进不去；
           * next(to.path) 表示重新在进来，目的是等上面异步更新路由表完毕；打一个时间差
           */
          //用户刷新，
          next(to.path)
        }else {
          next() // 表示放行（chain.doFilter(req, resp)）
        }
    }else {
        // 如果没有登录又不是去登录页面，那么直接打到登录页面
        next('/login')
    }
})


// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

// function convertRts(rts) {}
/**
 * 这是定义一个方法(与上面定义形式相同)，方法的目的是将路由表中所有的 component的字符串值
 *   转换成一个对应的组件形式，例如: 
 *     1.'Layout' => Layout
 *     2. 'user' =>  () => import('@/views/user/index')
 * 路由表示一个递归结构，所以程序需要递归。
 */
const convertRts = (rts) => {
  if(rts && rts.length > 0) {
    rts.forEach(rt => {
      // 因为服务端数据表的数据有些组件是 null, 所以为了程序的严谨，非null才转换
      if(rt.component) {
        if(rt.component === 'Layout') {
          rt.component = Layout
        }else {
          // 例如: rt.component为 'dept',
          //      转换之后的结果：rt.component = () => import('@/veiws/dept/index')
          rt.component = loadView(rt.component)
        }
        // 如果当前路由有子路由，那么需要将子路由进行转换
        if(rt.children) {
          convertRts(rt.children)
        }
      }
    })
  }
}

/**
 * 这个方法的写法上和下面是相同的，它的作用是将一个字符串转换为一个延迟加载的组件:
 *    loadView('user') 返回值：() => import('@/views/user/index')
 */
const loadView = (view) => {
    return (resolve) => require([`@/views/${view}/index`], resolve)
}
/** 
function loadView(view) {
  return (resolve) => require([`@/views/${view}/index`], resolve)
}
*/

export default router
